#include "platform.h"

#ifdef PublicClass
PlatformParams* ParamsPtr = new PlatformParams();
LOGGER_::Loger* LogerPtr = new LOGGER_::Loger();
#endif


map<string, ClientsInfo*> Platform::mClientsInfosMap = {};
std::mutex Platform::mMutexClientsInfosMap;
string Platform::mLogPath = "null";   // xx/xx/xxlog.txt
string Platform::mStartTime = "null";
int Platform::m_connectOutTime = 2;
int Platform::mClientsInfosMapMaxSize = 1000;
static void communication(void* arg);

Platform::Platform()
{
    cout << "create platform" << endl;

    string resInfo = "-1";
    Json::Value params;
    JsonTool::mReadJsonFromFile("../config/config.json", params);
    Json::Value paramsUsers;
    JsonTool::mReadJsonFromFile("../config/users.json", paramsUsers);
    

    vector<string> usersList;
    resInfo = JsonTool::mGetParamFromJson("users", paramsUsers,  usersList);
    mJudgetJsonIsExption(resInfo, "users");
    if(ParamsPtr->userMapPtr == NULL){
        ParamsPtr->userMapPtr = new std::map<std::string, std::string>();
    }
    for(unsigned int i=0; i<usersList.size(); i++){
        ParamsPtr->userMapPtr->insert(pair<string, string>(usersList[i], "xxx"));
    }
    
    resInfo = JsonTool::mGetParamFromJson("debug", params,  ParamsPtr->debug);
    mJudgetJsonIsExption(resInfo, "debug");
    resInfo = JsonTool::mGetParamFromJson("logPath", params,  ParamsPtr->logPath);
    mJudgetJsonIsExption(resInfo, "logPath");
    mStartTime = UtilsTools::mgetLocalTime_YMD(); 
    mLogPath = ParamsPtr->logPath+mStartTime+"_log.txt";
    resInfo = JsonTool::mGetParamFromJson("port", params,  ParamsPtr->port);
    mJudgetJsonIsExption(resInfo, "port");
    resInfo = JsonTool::mGetParamFromJson("ip", params,  ParamsPtr->ip);
    mJudgetJsonIsExption(resInfo, "ip");
    // strcpy(ParamsPtr->ip, ip.c_str());//复制s的内容到c中
    resInfo = JsonTool::mGetParamFromJson("queue", params,  ParamsPtr->queue);
    mJudgetJsonIsExption(resInfo, "queue");

    resInfo = JsonTool::mGetParamFromJson("helloBeatBuffSize", params,  ParamsPtr->helloBeatBuffSize);
    mJudgetJsonIsExption(resInfo, "helloBeatBuffSize");
    resInfo = JsonTool::mGetParamFromJson("hearBeatBuffSize", params,  ParamsPtr->hearBeatBuffSize);
    mJudgetJsonIsExption(resInfo, "hearBeatBuffSize");
    resInfo = JsonTool::mGetParamFromJson("workBeatBuffSize", params,  ParamsPtr->workBeatBuffSize);
    mJudgetJsonIsExption(resInfo, "workBeatBuffSize");

    resInfo = JsonTool::mGetParamFromJson("openDynamicImageCache", params,  ParamsPtr->openDynamicImageCache);
    mJudgetJsonIsExption(resInfo, "openDynamicImageCache");
    resInfo = JsonTool::mGetParamFromJson("tcpJsonFilePath", params,  ParamsPtr->tcpJsonFilePath);
    mJudgetJsonIsExption(resInfo, "tcpJsonFilePath");
    resInfo = JsonTool::mGetParamFromJson("sysJsonFilePath", params,  ParamsPtr->sysJsonFilePath);
    mJudgetJsonIsExption(resInfo, "sysJsonFilePath");


    Json::Value paramsProtocolNaming;
    JsonTool::mReadJsonFromFile("../config/protocolRule.json", paramsProtocolNaming);
    resInfo = JsonTool::mGetParamFromJson("WorkInfo", paramsProtocolNaming,  workInfo_);
    mJudgetJsonIsExption(resInfo, "WorkInfo");
    resInfo = JsonTool::mGetParamFromJson("HelloBeat", paramsProtocolNaming,  helloBeat_);
    mJudgetJsonIsExption(resInfo, "HelloBeat");
    resInfo = JsonTool::mGetParamFromJson("HelloACK", paramsProtocolNaming,  helloACK_);
    mJudgetJsonIsExption(resInfo, "HelloACK");

    resInfo = JsonTool::mGetParamFromJson("HeartBeat", paramsProtocolNaming,  heartbeat_);
    mJudgetJsonIsExption(resInfo, "HeartBeat");
    resInfo = JsonTool::mGetParamFromJson("HeartbeatACK", paramsProtocolNaming,  heartbeatACK_);
    mJudgetJsonIsExption(resInfo, "HeartbeatACK");

    resInfo = JsonTool::mGetParamFromJson("ErrorInfo", paramsProtocolNaming,  errorInfo_);
    mJudgetJsonIsExption(resInfo, "ErrorInfo");

    ThreadPool* pool = threadPoolCreate(3, 10, 10, ParamsPtr->debug);
    ParamsPtr->poolPtr = pool;
    RT::WorkerParams wp;
    wp.threadPool = pool;
    wp.configPath = "../config/configWorker.json";
    mWorkerPtr = make_shared<RT::Worker>(wp);   // 创建与上位机通信实例

    //将连接请求任务写入任务队列执行
    const string clientIp = "0.0.0.0";
    ClientsInfo* clientInfoP = mGetClientsInfo(clientIp);
    if(clientInfoP == NULL){
        ClientsInfo clientInfo;
        clientInfo.setId(clientIp);
        clientInfo.setIp(clientIp);
        clientInfo.setPort(ParamsPtr->port);
        clientInfo.setName("accept function");
        clientInfo.setTaskBrief("连接请求任务处理");
        clientInfo.setUse(true);
        clientInfo.setPoolPtr(pool);
        clientInfo.setPlatformPtr(this);
        mSetClientsInfo(clientInfo.getId(), &clientInfo);
    }
    addTask2Pool(ParamsPtr->poolPtr = pool, Platform::mAcceptConn, mGetClientsInfo(clientIp), "accept", false);

}
Platform::~Platform(){
    mExitSocket();  //用于退出accept工作线程
    m_setExit(true);
    if(ParamsPtr->poolPtr != NULL){
        threadPoolDestroy(ParamsPtr->poolPtr, 3);
        ParamsPtr->poolPtr = NULL;
    }
}

void Platform::mStart(const PlatformParams params){

}

/**
 * @brief 获得map是否有该id的连接
 * @param input string key
 * @return ClientsInfo*, 成功：ClientsInfo*， 失败：NULL
*/
ClientsInfo*  Platform::mGetClientsInfo(const string key)
{
    map<string, ClientsInfo*>::iterator iter;
    unique_lock<mutex> lock(mMutexClientsInfosMap);
    iter = mClientsInfosMap.find(key);
    if(iter != mClientsInfosMap.end()){
        return mClientsInfosMap[key.c_str()];
    }
    else{
        // PRINTINFO("\033[1;32mthis key don't exist, and will create it\033[0m\n");
        return NULL;
    }
}
/**
 * @brief 如果key存在，则覆盖
 * @param input string key
 * @param input ClientsInfo* clientInfo
 * @return int, 0：成功， -1：失败
*/
int  Platform::mSetClientsInfo(const string key, ClientsInfo* clientInfo)
{
    map<string, ClientsInfo*>::iterator iter;
    unique_lock<mutex> lock(mMutexClientsInfosMap);
    iter = mClientsInfosMap.find(key);
    // //iter->second != NULL
    //存在，则覆盖
    if(iter != mClientsInfosMap.end()){
        if(mClientsInfosMap[key.c_str()] != NULL){
            // delete mClientsInfosMap[key.c_str()];
            // mClientsInfosMap[key.c_str()] = new ClientsInfo(*clientInfo);
            mClientsInfosMap[key.c_str()]->upData(*clientInfo);
        }
        return 0;
    }
    else{
        //创建
        if(mClientsInfosMap.size() < mClientsInfosMapMaxSize){
            mClientsInfosMap[key.c_str()] = new ClientsInfo(*clientInfo);
            return 0;
        }
        for(iter=mClientsInfosMap.begin(); iter != mClientsInfosMap.end(); iter++){
            if( !iter->second->getUse()){
                iter->second->upData(*clientInfo);
                return 0;
            }
        }
        boost::format ft = boost::format("mClientsInfosMap is full: %1%, max:%2%\n") % mClientsInfosMap.size() % mClientsInfosMapMaxSize ; 
        LogerPtr->m_WARNING(BoostCodeLocal+ft.str(), mLogPath);
        return -1;
    }
}

/**
 * @brief 清除mClientsInfosMap的某个元素
 * @param const string key
*/
void  Platform::mDelClientsInfo(const string key)
{
    map<string, ClientsInfo*>::iterator iter;
    unique_lock<mutex> lock(mMutexClientsInfosMap);
    iter = mClientsInfosMap.find(key);
    // //iter->second != NULL
    //存在，则删除
    if(iter != mClientsInfosMap.end()){
        if(iter->second != NULL && !iter->second->getUse()){
            delete iter->second;
        }
        mClientsInfosMap.erase(iter->first );
        return ;
    }
    else{
        LogerPtr->m_ERROR(BoostCodeLocal+iter->first+" clientInfo is NULL or the use prop is true, don't be removed\n", mLogPath);
        return ;
    }
}
/**
 * @brief 清空mClientsInfosMap
*/
void  Platform::mClearClientsInfo()
{
    map<string, ClientsInfo*>::iterator iter;
    unique_lock<mutex> lock(mMutexClientsInfosMap);
    for (auto iter : mClientsInfosMap) {
        cout << "clear: " << iter.first  << endl;
        if(iter.second != NULL && !iter.second->getUse()){
            delete iter.second; 
        }
        else{
            LogerPtr->m_ERROR(BoostCodeLocal+iter.first+" clientInfo is NULL or the use prop is true, don't be removed\n", mLogPath);
        }
    }
    mClientsInfosMap.clear();
}

/**
 * @brief 检查本地json属性是否读取成功，当传参不等于 "0"， 表示读取本都json属性失败
*/
void Platform::mJudgetJsonIsExption(const string& info, const string key)
{
    if(info != "0"){
        mLogDIWE(BoostCodeLocal+"本地json属性读取失败！！！ "+key+"\n", "error");
    }
}
/**
 * @brief 将error信息写入到本都log.txt, 当使用error模式时，会结束程序 
 * @param input const string& info, BoostCodeLocal+info, 在传参前加BoostCodeLocal，显示报错位置
 * @param input string logPath, 写入的路径("xxx/xx/log.txt"), 默认为空， 则使用系统默认路径mLogPath
 * @param input const string mode, 日志类型: info， debug, warn, error
 * @param string ft = boost::format("load .ini: %s camera_up: %s, camera_down: %s\n") %robotname.c_str() %m_camera_up.c_str() %m_camera_down.c_str();
*/
void Platform::mLogDIWE(const string& info, string logPath, const LOGGER_::Log_level level)
{
    if(logPath == ""){
        logPath = mLogPath;
    }
  
    switch (level)
    {
    case LOGGER_::info:
        LogerPtr->m_INFO(info, logPath, true, true);
        break;
    case LOGGER_::debug:
        LogerPtr->m_DEBUG(info, logPath);
        break;
    case LOGGER_::warning:
        LogerPtr->m_WARNING(info, logPath, true, false);
        break;
    case LOGGER_::error:
        LogerPtr->m_ERROR(info, logPath, true, true);
        exit(0);
        break;
    default:
        LogerPtr->m_ERROR("mLogDIWE 输入的mode 异常", logPath, true, true);
        exit(0);
        break;
    }
    
}

/**
 * @brief socket服务端等待连接请求处理方法
 * @see 跳转相关函数
 * @warning xx
 * @param input void* arg, 传入ClientsInfo*类型
*/
void Platform::mAcceptConn(void* arg)
{
    //load param
    const string selfName = "acceptConn  say: ";
    cout << selfName << " i am running " << endl;
    ClientsInfo _clientInfo = *(ClientsInfo*)arg;
    int port = -1;
    int queue = 20;
    if(ParamsPtr != NULL){
        port = ParamsPtr->port;
        queue = ParamsPtr->queue;
    }
    else{
        LogerPtr->m_ERROR(BoostCodeLocal+"ParamsPtr = NULL\n", mLogPath);
        exit(0);
    }

    int  sockfd_service;
    sockfd_service = socket(AF_INET, SOCK_STREAM, 0);
    if( sockfd_service == -1 ){      //AF_INET: IPV4   ;SOCK_STREAM: TCP, //若成功则返回一个sockfd (套接字描述符)
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        return;
    }
    const int on = 1;
    if(setsockopt(sockfd_service, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){
        printf("setsockopt error: %s(errno: %d)\n",strerror(errno),errno);
        return;
    }

    struct sockaddr_in  servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);
    printf("port: %d\n", port);

    if( bind(sockfd_service, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
        return;
    }

    if( listen(sockfd_service, queue) == -1){
        perror("listen");
        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
        return;
    }
 
    while (1)
    {
        socklen_t addrlen = sizeof(struct sockaddr_in);
   
        //成功返回非负描述字，出错返回-1
        /* wait and accept the request of client. 
        if success,then return a new sockfd, otherwise return -1 for fail.
        and if no connect request, it will block in here.
        */ 
        const int connfd = accept(sockfd_service, (struct sockaddr*)&servaddr, &addrlen);
        
        if(connfd == -1){
            perror("accept");
            continue;
        }

        const string clientIp = inet_ntoa(servaddr.sin_addr);
        const int clientPort = ntohs(servaddr.sin_port);
        ClientsInfo clientInfoHello;
        clientInfoHello.setConnfd(connfd);
        clientInfoHello.setIp(clientIp);
        std::string res = "null";
        res = mHelloConnectClient(&clientInfoHello);      //if success, name has been modified
        if ( res != "0") {
            //表示握手失败
            LogerPtr->m_WARNING(BoostCodeLocal + "hello failed\n", mLogPath, true, false);
            close(connfd);
            continue;
        }

        if(clientInfoHello.getId() == "EXIT" || clientInfoHello.getName() == "EXIT"){
            LogerPtr->m_INFO(BoostCodeLocal + "this is EXIT connect, exit accept()\n", mLogPath, true, true);
            close(connfd);
            break;
        }
    
        clientInfoHello.setPort(_clientInfo.getPort() );
        clientInfoHello.setPoolPtr(ParamsPtr->poolPtr);
        clientInfoHello.setPlatformPtr(_clientInfo.getPlatformPtr() );
        clientInfoHello.setUse(true);
        string setID = clientInfoHello.getId();
        ClientsInfo* clientInfoPtr = mGetClientsInfo(clientIp);
        if(clientInfoPtr != NULL && clientInfoPtr->getUse()){
            //已存在，且正在运行，则拒绝连接
            Json::Value refuseJs;
            refuseJs["Error"]["data"] = "该 id 已存在，该次请求拒绝";
            refuseJs["Error"]["time"] = UtilsTools::mgetLocalTime_YMDHMS();
            const std::string refuseJs_str = "*****" + JsonTool::mJson2string(refuseJs) + "#####";
            send(connfd, refuseJs_str.c_str(), strlen(refuseJs_str.c_str()) ,0);
            close(connfd);
            LogerPtr->m_WARNING(BoostCodeLocal+"该 id 已存在，该次请求拒绝\n", mLogPath, true, false);
            continue;
        }
        //存在，但已停止或创建新的，则建立连接， 备注：其停止只能有在任务结束时操作
        int isSet = mSetClientsInfo(setID, &clientInfoHello);
        ClientsInfo* ci = mGetClientsInfo(setID);
        if(ci != NULL && ci->getUse()){
            //ClientsInfo* 放入队列后，其内存的改动必须由其对应的方法执行，****
            addTask2Pool(ParamsPtr->poolPtr, communication, mGetClientsInfo(setID), ci->getTaskId(), false);
        }
        else{
            LogerPtr->m_WARNING(BoostCodeLocal+"clientsInfo 创建失败\n", mLogPath);
            continue;
        }


        boost::format ft = boost::format("service say: client info: \n\t\t name:%s \n\t\t ip:%s \n\t\t port:%d  \n\t\t connfd:%d \n\t\t taskBrief:%s \
                                        ") \
                % clientInfoHello.getName().c_str() 
                % inet_ntoa(servaddr.sin_addr) 
                % ntohs(servaddr.sin_port) 
                % connfd
                % clientInfoHello.getTaskBrief().c_str(); 
        LogerPtr->m_INFO(ft.str(), "", true, false);
    }//while

    

    
    
    close(sockfd_service);
    this_thread::sleep_for(chrono::seconds(1));
    return ;
}

/**
 * @brief 初次建立连接，通过握手包确认身份，
 * @param input/output ClientsInfo* helloInfo
 * @return 通过："0", 不通过：errorInfo
*/
string Platform::mHelloConnectClient(ClientsInfo* helloInfo)
{
    const string selfName = "mhelloConnectClient:: ";
    const int connfd = helloInfo->getConnfd();

    // hello connect
    const int size_temp = ParamsPtr->helloBeatBuffSize;
    const std::string helloLogPath = ParamsPtr->logPath + mStartTime+ "_hello_log.txt";
    char buffer_buf[size_temp];       //
    memset(buffer_buf, 0 ,sizeof(buffer_buf));
    int len = recv(connfd, buffer_buf, sizeof(buffer_buf), 0);
    const string bufferStr = buffer_buf;
    boost::format ft = boost::format("recv hello beat %1% :\n%2%\n <<<\n") % strlen(buffer_buf) % bufferStr; 
    LogerPtr->m_INFO(BoostCodeLocal + ft.str(), helloLogPath);

    if(len == 0){
        LogerPtr->m_WARNING(BoostCodeLocal+"hello(): recv size = 0 \n", helloLogPath);
        return "1";
    }
    else if (len < 0){
        LogerPtr->m_WARNING(BoostCodeLocal + "hello(): recv size < 0\n", helloLogPath);
        return "2";
    }
    SocketDataParse socketDataParse(1024);  // 5mb
    std::vector<struct RecvInfo> vec_recvInfos;
    string res = "-1";
    try{
        res = socketDataParse.m_socketDataToJsons(connfd, buffer_buf, len, vec_recvInfos);   //len <= size_temp, 传入len更安全
    }
    catch (std::exception& e)
    {  
        LogerPtr->m_ERROR(BoostCodeLocal + "m_socketDataToJsons exception\n", mLogPath);
        exit(-1);
    } 
    
    if( res != "0"){
        LogerPtr->m_WARNING(BoostCodeLocal + selfName+">>> hello(): recv buff has a problems that cannot be parsed correctly\n"+res, helloLogPath);
        return "3";
    }
    for(unsigned int i=0; i<vec_recvInfos.size(); i++){
        const Json::Value &root = vec_recvInfos[i].root;
        const string &imgBuff_string = vec_recvInfos[i].imgDataBuf_str;

        //check img exist successfully， 一般hello包一般不会用到
        if(imgBuff_string != "null"){
            if(imgBuff_string.size() < 10){
                boost::format ft = boost::format("recv img size error: %lu\n")%imgBuff_string.size();
                LogerPtr->m_WARNING(ft.str(), helloLogPath);
                //一般hello包一般不会用到, 该处出错，不用跳过，直接解析它的json部分即可
            }
        }
        
        if( root.isMember("Hello") ){
            const Json::Value hello = root["Hello"];
            string helloRes = "-1";
            string robotId = "null";
            string robotName = "null";
            std::string taskId = "null";
            string taskBrief = "null";
            string type = "null";
    
            helloRes = JsonTool::mGetParamFromJson("type", hello, type);
            if(helloRes != "0"){
                LogerPtr->m_WARNING(BoostCodeLocal+"hellobeat has problems that 'type' is null or error:\n", helloLogPath);
                continue;
            }
            helloRes = JsonTool::mGetParamFromJson("id", hello, robotId);
            if(helloRes != "0"){
                LogerPtr->m_WARNING(BoostCodeLocal+"hellobeat has problems that 'id' is null or error:\n", helloLogPath);
                continue;
            }

            helloRes = JsonTool::mGetParamFromJson("name", hello, robotName);
            if(helloRes != "0"){
                LogerPtr->m_WARNING(BoostCodeLocal+"hellobeat has problems that 'name' is null or error:\n", helloLogPath);
                continue;
            }
            helloRes = JsonTool::mGetParamFromJson("taskBrief", hello, taskBrief);
            if(helloRes != "0"){
                LogerPtr->m_WARNING(BoostCodeLocal+"hellobeat has problems that 'taskBrief' is null or error:\n", helloLogPath);
                continue;
            }

            helloRes = JsonTool::mGetParamFromJson("taskId", hello, taskId);
            if(helloRes != "0"){
                LogerPtr->m_WARNING(BoostCodeLocal+"hellobeat has problems that 'taskId' is null or error:\n", helloLogPath);
                continue;
            }
          
            //身份验证
            if(ParamsPtr->userMapPtr->find(robotId) == ParamsPtr->userMapPtr->end() ){
                LogerPtr->m_WARNING(BoostCodeLocal+robotId+": hellobeat id is no access, connect refused!!!\n", helloLogPath);
                continue;
            }

            Json::Value helloAck;
            helloAck["HelloACK"] = hello;
            helloAck["HelloACK"]["time"] = UtilsTools::mgetLocalTime_YMDHMS();
            const std::string helloBeat_buf_ACK = "*****" + JsonTool::mJson2string(helloAck) + "#####";
            send(connfd, helloBeat_buf_ACK.c_str(), strlen(helloBeat_buf_ACK.c_str()) ,0);

            helloInfo->setType(type);
            helloInfo->setId(robotId);
            helloInfo->setName(robotName);
            helloInfo->setTaskId(taskId);
            helloInfo->setTaskBrief(taskBrief);

            return "0";
        }//hello

        LogerPtr->m_WARNING("'Hello' is null, his is not a helloBeat\n", helloLogPath);
    }  
    return "4";
}

/**
 * @brief 用于退出accept()的阻塞，结束其工作线程
*/
int Platform::mExitSocket()
{
    const string selfName = "mExitSocket:  ";
    //ctreate socket for conmmunication
    const int connfd = socket(AF_INET, SOCK_STREAM, 0);
    if( connfd == -1 ){      //AF_INET: IPV4   ;SOCK_STREAM: TCP, //若成功则返回一个sockfd (套接字描述符)
        boost::format ft = boost::format("create socket error: %1%(errno: %2%)\n") % strerror(errno) %errno; 
        LogerPtr->m_ERROR(BoostCodeLocal + ft.str(), mLogPath);
        exit(-1);
    }

    //connect service with ip and port
    struct sockaddr_in  servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, const_cast<char*>(ParamsPtr->ip.c_str()) , &servaddr.sin_addr.s_addr);
    servaddr.sin_port = htons(ParamsPtr->port);

    Json::Value root;
    JsonTool::mReadJsonFromFile(ParamsPtr->tcpJsonFilePath+"exitHelloBeat.json",root);
    root["Hello"]["time"] = UtilsTools::mgetLocalTime_YMDHMS();
    Json::Value helloJS;
    helloJS["Hello"] = root["Hello"];
    const string helloBeart_buf = "*****" + JsonTool::mJson2string(helloJS) + "#####";
    int count = 0;
    while (1)
    {   
        count ++;
        /* code */
        int res = connect(connfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
        if(res == -1 && count < 10){
            perror("exit connect() \n");
            sleep(1);
            continue;
        }
        if(res == 0){
            break;
        }else{
            LogerPtr->m_ERROR(BoostCodeLocal+"exitSocket failed\n", mLogPath);
            exit(0);
        }
    }
    //hello packet
    send(connfd, helloBeart_buf.c_str(), strlen(helloBeart_buf.c_str() ) , 0);//向TCP连接的另一端发送数据。

    char helloBeat_buf_recv[300];
    memset(helloBeat_buf_recv, 0 ,sizeof(helloBeat_buf_recv));
    const int len = recv(connfd, helloBeat_buf_recv, sizeof(helloBeat_buf_recv), 0);
    if(len == 0){
        LogerPtr->m_WARNING(BoostCodeLocal + "connect break off \n", mLogPath);
      
    }
    else if (len < 0){
        LogerPtr->m_ERROR(BoostCodeLocal + "recv error\n", mLogPath);
    
    }
    
    struct RecvInfo strucRectInfo_exit;
    strucRectInfo_exit.imgDataBuf_str = "null";
    Json::Value js;

    const string resInfo = JsonTool::mString2jsonFromSocketBeat(js, helloBeat_buf_recv, len);
    if( resInfo == "0" ){
        if( js.isMember("HelloACK") ){
            LogerPtr->m_INFO("exit socket->accept() successfully \n", mLogPath);
        }
    }
    else{
        LogerPtr->m_WARNING(BoostCodeLocal+resInfo+" \n", mLogPath,true,false);
    }
    sleep(0.5);
    close(connfd);
    return 0;

}

void Platform::m_setExit(const bool& val){
    unique_lock<mutex> lock(m_mutexExit);
    m_isExit = val;
}
bool Platform::m_getExit(){
    unique_lock<mutex> lock(m_mutexExit);
    return m_isExit;
}

/**
 * @brief 放入到任务队列中，等待执行， 执行完毕后，自行关闭传参的指针内容
 * @param input void* arg, 需转为ClientsInfo*
*/
void communication(void* arg)
{
    ClientsInfo clientInfo = *(ClientsInfo*)arg;
    const int connfd = clientInfo.getConnfd();
    ThreadPool* pool = clientInfo.getPoolPtr();
    Platform* platformPtr = clientInfo.getPlatformPtr();

    if(platformPtr == NULL || pool == NULL){
        LogerPtr->m_ERROR("platformPtr == NULL || pool == NULL");
        return;
    }
    const int connectOutTime = Platform::m_connectOutTime;
    const bool isDebug = ParamsPtr->debug;
    char buffer_buf[500]; 
    SocketDataParse socketDataParse(52428800);  // 5mb

    fd_set rfds, wfds, efds;
    struct timeval tv;
    int retval, maxfd;
    // && !getShutdown(pool)
    while( !getShutdown(pool)  ){
        /*把可读文件描述符的集合清空*/
        FD_ZERO(&rfds);
        /*把标准输入的文件描述符加入到集合中*/
        FD_SET(0, &rfds);
        maxfd = 0;
        /*把当前连接的文件描述符加入到集合中*/
        FD_SET(connfd, &rfds);
        /*找出文件描述符集合中最大的文件描述符*/
        if(maxfd < connfd) maxfd = connfd;
        /*设置超时时间*/   //must place in while{} 
        tv.tv_sec = connectOutTime; // /s
        tv.tv_usec = 0;
        
        /*等待聊天*/
        retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
        if(retval == -1){
            printf("select出错，客户端程序退出\n");
            break;
        }
        else if(retval == 0){
			if(isDebug){
				printf("within %d seconds, 服务器没有任何输入信息，并且客户端也没有信息到来，waiting...\n", tv.tv_sec);
			}
            continue;
        }
        else if(FD_ISSET(connfd, &rfds)){
            // recv message
            memset(buffer_buf, 0 ,sizeof(buffer_buf));
            int len = recv(connfd, buffer_buf, sizeof(buffer_buf), 0);
            const string bufferStr = buffer_buf;

            if(len == 0){
                LogerPtr->m_WARNING(BoostCodeLocal+"communication(): recv size = 0 \n", Platform::mLogPath, true, false);
                break; ;
            }
            else if (len < 0){
                LogerPtr->m_WARNING(BoostCodeLocal + "communication(): recv size < 0\n", Platform::mLogPath, true, false);
                break; ;
            }
            
            std::vector<struct RecvInfo> vec_recvInfos;
            string res = "-1";
            try{
                res = socketDataParse.m_socketDataToJsons(connfd, buffer_buf, len, vec_recvInfos);   //len <= size_temp, 传入len更安全
            }
            catch (std::exception& e)
            {  
                LogerPtr->m_ERROR(BoostCodeLocal + "SocketDataParse exception\n", Platform::mLogPath);
                Json::Value errorJs;
                errorJs["error"]["info"] = "SocketDataParse exception";
                errorJs["error"]["time"] = UtilsTools::mgetLocalTime_YMDHMS();
                const std::string errorJsStr = "*****" + JsonTool::mJson2string(errorJs) + "#####";
                clientInfo.mSend(connfd, errorJsStr);
                continue;
            } 
        
            if( res != "0"){
                LogerPtr->m_WARNING(BoostCodeLocal +">>> communication(): recv buff has a problems that cannot be parsed correctly\n", Platform::mLogPath, true,false);
                break;
            }
            for(unsigned int i=0; i<vec_recvInfos.size(); i++){
                const Json::Value &root = vec_recvInfos[i].root;
                const std::string rootStr = vec_recvInfos[i].rootStr;
                const string &imgBuff_string = vec_recvInfos[i].imgDataBuf_str;

                //check img exist successfully， 一般hello包一般不会用到
                if(imgBuff_string != "null"){
                    if(imgBuff_string.size() < 10){
                        boost::format ft = boost::format("recv img size error: %lu\n")%imgBuff_string.size();
                        LogerPtr->m_WARNING(ft.str(), "", true, false);
                        //一般hello包一般不会用到, 该处出错，不用跳过，直接解析它的json部分即可
                    }
                    else if(isDebug){
                        UtilsTools::mSaveImage(imgBuff_string, "./test.jpg");
                    }
                } 

                
                
                if( root.isMember(platformPtr->heartbeat_) ){
                    if(isDebug){
                        LogerPtr->m_DEBUG(BoostCodeLocal + rootStr, "", true, false);
                    }
                     
                    Json::Value heartBeatJs;
                    heartBeatJs[platformPtr->heartbeatACK_] = root[platformPtr->heartbeat_];
                    heartBeatJs[platformPtr->heartbeatACK_]["time"] = UtilsTools::mgetLocalTime_YMDHMS();
                    const std::string heartBeat_buf_ACK = "*****" + JsonTool::mJson2string(heartBeatJs) + "#####";
                    clientInfo.mSend(connfd, heartBeat_buf_ACK);
                    continue;
                }
                if( root.isMember(platformPtr->workInfo_) ){
                    //解析json中的目标id，从而获取该targetId的connfd，并将该次接收的信息发送到connfd
                    const string targetId = root[platformPtr->workInfo_]["targetId"].asString();
                    const string selfId = root[platformPtr->workInfo_]["selfId"].asString();
                    const string callbackId = root[platformPtr->workInfo_]["callbackId"].asString();
                    const Json::Value data = root[platformPtr->workInfo_]["data"];

                    if(isDebug){
                        LogerPtr->m_DEBUG(selfId+": recv workInfo: "+ rootStr, "");
                    }

                    if(targetId == "null"){
                        continue;
                    }

                    std::string rootStr_ = "";
                    if(imgBuff_string != "null" && imgBuff_string.size() > 100){
                        rootStr_ = "*****" + rootStr + "#####"+imgBuff_string;
                    }
                    else{
                        rootStr_ = "*****" + rootStr + "#####";
                    }

                    //targetId指向上位机
                    if(targetId == "ui")
                    {
                        platformPtr->mWorkerPtr->mSendStr2service(platformPtr->mWorkerPtr->mConnfd, rootStr_);
                        continue;
                    }

                    //targetId指向robot，或server
                    ClientsInfo* ciPtr = Platform::mGetClientsInfo(targetId);
                    if(ciPtr != NULL && ciPtr->getUse() ){
                        const int targetConnfd = ciPtr->getConnfd();
                        clientInfo.mSend(targetConnfd, rootStr_);
                    }
                    else{
                        LogerPtr->m_WARNING(targetId+":被请求，但该目标不在线！！！", "", true, false);
                        Json::Value WainningJs;
                        WainningJs[platformPtr->errorInfo_]["info"] = targetId+"：该目标不在线！！！";
                        WainningJs[platformPtr->errorInfo_]["time"] = UtilsTools::mgetLocalTime_YMDHMS();
                        const std::string WainningJs_str = "*****" + JsonTool::mJson2string(WainningJs) + "#####";
                        // send(connfd, WainningJs_str.c_str(), strlen(WainningJs_str.c_str()) ,0);
                        clientInfo.mSend(connfd, WainningJs_str);
                    }
                    continue;
                }
                boost::format ft = boost::format("json中没有识别到有效的key: %s\n") % rootStr.c_str();
                LogerPtr->m_WARNING(BoostCodeLocal+ft.str() );
               
            }
        }
    }//while
    clientInfo.setUse(false);
    LogerPtr->m_INFO(clientInfo.getName()+": connect break off\n", "", true, false);
    close(connfd);
}

